package com.lap.mybatis.starter.core;

import java.util.List;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.ListUtils;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;

/**
 * 批量操作执行期
 *
 * @author Shuisheng.Lao
 * @version 0.0.1
 */
@Slf4j
public record BatchExecutor(SqlSessionFactory sqlSessionFactory) {

  /**
   * 执行批量操作
   *
   * @param statementId mapper id
   * @param dataList 数据集合
   * @param commitQuantity 提交数量（每批处理的数量）
   * @param rollback 异常是否外抛
   * @return 成功处理的记录数量
   */
  public int executeBatch(
      String statementId, List<Object> dataList, int commitQuantity, boolean rollback) {

    if (CollectionUtils.isEmpty(dataList)) {
      return 0;
    }

    int successCount = 0;

    try (SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH, false)) {
      List<List<Object>> partitions = ListUtils.partition(dataList, commitQuantity);

      for (int i = 0; i < partitions.size(); i++) {
        try {
          List<Object> batch = partitions.get(i);
          for (Object item : batch) {
            session.insert(statementId, item);
          }

          // 每批处理完成后刷新并清空缓存
          session.flushStatements();
          session.clearCache();

          successCount += batch.size();

        } catch (Exception e) {
          if (rollback) {
            // 回滚当前会话的所有操作
            session.rollback();
            throw new RuntimeException("批量操作在第 " + (i + 1) + " 批处理时失败", e);
          } else {
            log.error("第 {} 批处理失败，跳过此批次", i + 1, e);
            // 当前批次失败，但继续处理下一批
            session.flushStatements();
            session.clearCache();
          }
        }
      }

      // 提交所有成功的操作
      session.commit();
      return successCount;

    } catch (Exception e) {
      if (rollback) {
        throw new RuntimeException("批量操作执行失败", e);
      } else {
        log.error("批量操作执行失败", e);
        return successCount; // 返回已成功处理的数量
      }
    }
  }
}
